<?php
declare (strict_types=1);

namespace app\shop\controller;

use app\common\library\token\TokenExpirationException;
use app\shop\exception\AuthException;
use app\shop\exception\AuthExpireException;
use app\shop\exception\FailException;
use app\shop\exception\ParamsErrorException;
use app\shop\library\response\Response;
use app\shop\library\response\ResponseCode;
use app\shop\library\ShopAuth;
use app\shop\library\ShopHelper;
use Psr\Http\Message\StreamInterface;
use think\db\exception\PDOException;
use think\exception\ValidateException;
use think\facade\Db;
use think\Model;
use think\model\Collection;
use think\Paginator;
use think\response\Json;
use Throwable;

#[\AllowDynamicProperties]  //兼容php8.2弃用动态属性创建
class BaseController
{
    /**
     * @var string 验证器类名
     */
    protected string $validateClassName = '';

    /**
     * @var array 不需要登录的方法
     */
    protected array $noNeedLogin = [];

    /**
     * 权限类实例
     * @var ShopAuth
     */
    protected ShopAuth $auth;

    /**
     * 应用站点系统设置
     * @var bool
     */
    protected bool $useSystemSettings = true;

    /**
     * 初始化
     * @throws AuthException
     * @throws AuthExpireException
     * @throws FailException
     * @throws Throwable
     */
    public function __construct()
    {
        // 系统站点配置
        if ($this->useSystemSettings) {
            // 检查数据库连接
            try {
                Db::execute("SELECT 1");
            } catch (PDOException $e) {
                throw new FailException(mb_convert_encoding($e->getMessage(), 'UTF-8', 'UTF-8,GBK,GB2312,BIG5'));
            }

            ip_check(); // ip检查
            set_timezone(); // 时区设定
        }
        request()->filter('filter');

        $needLogin = !action_in_arr($this->noNeedLogin);

        $this->auth = ShopAuth::instance();
        try {
            $token = get_auth_token(['ba', 'user', 'token']);
            if ($token) $this->auth->init($token);
        } catch (TokenExpirationException) {
            if ($needLogin) {
                throw new AuthExpireException();
            }
        }

        if ($needLogin && !$this->auth->isLogin()) {
            throw new AuthException();
        }
    }

    /**
     * 获取get请求数据
     * @param bool $withUserId 是否加入用户ID到参数中
     * @return array
     */
    public function getInput(bool $withUserId = false): array
    {
        return $this->input('get', $withUserId);
    }

    /**
     * 获取post请求数据
     * @param bool $withUserId 是否加入用户ID到参数中
     * @return array
     */
    public function postInput(bool $withUserId = false): array
    {
        return $this->input('post', $withUserId);
    }

    /**
     * 获取请求数据
     * <br>处理：键值驼峰转下划线、去除请求值前后空格
     * @param string $method     只获取指定请求方式的数据
     * @param bool   $withUserId 是否加入用户ID到参数中
     * @return array
     */
    private function input(string $method = 'get', bool $withUserId = false): array
    {
        $input = [];

        $requestMethod = strtoupper(request()->method());

        if ($requestMethod !== strtoupper($method)) {
            return $input;
        }

        $input = input($method . '.');

        if ($withUserId) {
            $input['user_id'] = ShopAuth::instance()->id;
        }

        $input = ShopHelper::hump_to_underline_array($input);

        return ShopHelper::trim_array($input);
    }

    /**
     * 数据校验
     * @param array  $data  校验数据
     * @param string $scene 校验场景
     * @throws ParamsErrorException
     */
    protected function validateData(array $data, string $scene = ''): void
    {
        try {
            $validate = validate($this->validateClassName);
            if ($scene) {
                $validate->scene($scene);
            }
            $validate->check($data);
        } catch (ValidateException $e) {
            throw new ParamsErrorException($e->getError());
        }
    }

    /**
     * 请求成功
     * @param mixed  $data 响应数据
     * @param string $msg  响应信息
     * @return StreamInterface|Json
     */
    public function success(mixed $data = [], string $msg = ''): StreamInterface|Json
    {
        if ($data instanceof \GuzzleHttp\Psr7\Response) {
            return $data->getBody();
        }

        if ($data instanceof Model || $data instanceof Paginator || $data instanceof Collection) {
            $data = $data->toArray();
        }

        if (is_string($data) && empty($msg)) {
            return Response::buildJson(ResponseCode::SUCCESS['code'], [], $data);
        }

        return Response::buildJson(ResponseCode::SUCCESS['code'], $data, $msg);
    }

    /**
     * 请求失败
     * @param string $msg  响应信息
     * @param array  $data 额外响应数据
     */
    public function fail(string $msg = ResponseCode::FAIL['msg'], array $data = []): Json
    {
        return Response::buildJson(ResponseCode::FAIL['code'], $data, $msg);
    }
}
